/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.logging;

import org.zhiqim.kernel.Z;
import org.zhiqim.kernel.annotation.AnNullable;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.config.Item;
import org.zhiqim.kernel.logging.appender.ConsoleAppender;
import org.zhiqim.kernel.logging.appender.FileDailyAppender;
import org.zhiqim.kernel.logging.appender.FileAppender;
import org.zhiqim.kernel.logging.logger.ZhiqimLogger;
import org.zhiqim.kernel.model.maps.HashMapCV;
import org.zhiqim.kernel.model.maps.HashMapSV;
import org.zhiqim.kernel.service.Servicer;
import org.zhiqim.kernel.util.Arrays;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;

/**
 * 标准日志服务
 *
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
public class LogServer extends Servicer implements LogService
{
    private static final HashMapSV<LoggerWapper> mustNameMap = new HashMapSV<>();
    private static final HashMapCV<LoggerWapper> mustClassMap = new HashMapCV<>();
    private static final LoggerWapper defaultWapper = new LoggerWapper();
    
    @Override
    public boolean create() throws Exception
    {
        if (!Z.conf().hasGroup(id))
        {
            System.out.println("日志配置["+id+"]不存在");
            return false;
        }
        
        //查出配置列表
        for (Item item : Z.conf().group(id).list())
        {
            String key = item.getKey();
            if (!Z.conf().hasGroup(key))
            {
                System.out.println("日志配置["+key+"]不存在");
                return false;
            }
            
            String value = item.getString();
            if (Validates.isEmpty(value) || value.indexOf("-") == -1)
            {
                System.out.println("日志配置["+key+"]的value(级别)不正确，格式如debug-info");
                return false;
            }
            
            String[] levels = Arrays.toStringArray(value, "-");
            int levelMin = LogFactory.getLevel(levels[0]);
            int levelMax = LogFactory.getLevel(levels[1]);
            if (levelMin == 0 || levelMax == 0 || levelMax < levelMin)
            {
                System.out.println("日志配置["+key+"]级别配置有误，最小和最大值必须在(debug|info|error|warn|fatal)范围内，如error-fatal");
                return false;
            }
            
            //缺省文件日志路径如key=logging.info，则filePath=./logs/info.log
            String filePathDefault = "./logs/"+ Strings.trimLeft(key, id+".") +".log";
            
            Group group = Z.conf().group(key);
            String mustName = group.getString(MUST_NAME);
            String mustClass = group.getString(MUST_CLASS);
            
            LogConfig config = new LogConfig();
            config.setId(key);
            config.setLevelMin(levelMin);
            config.setLevelMax(levelMax);
            config.setConsoleOut(group.isTrue(CONSOLE_OUT));
            config.setConsolePattern(group.getString(CONSOLE_PATTERN, PATTERN_DEFAULT));
            config.setFileOut(group.isTrue(FILE_OUT));
            config.setFilePath(group.getString(FILE_PATH, filePathDefault));
            config.setFilePattern(group.getString(FILE_PATTERN, PATTERN_DEFAULT));
            config.setFileEncoding(group.getString(FILE_ENCODING, _UTF_8_));
            config.setFileMaxNum(group.getInt(FILE_MAX_NUM, 7));
            config.setFileMaxSize(group.getInt(FILE_MAX_SIZE, 100));
            config.setMustName(mustName);
            
            if (Validates.isNotEmptyBlank(mustClass))
            {
                Class<?> mustClazz = Z.cls().forName(mustClass);
                config.setMustClass(mustClazz);
            }
            
            if (config.isConsoleOut())
            {//支持输出到控制台
                config.setConsoleAppender(new ConsoleAppender(config.getConsolePattern()));
            }
            
            if (config.isFileOut())
            {//支持输出到文件
                String appenderClass = group.getString(FILE_APPENDER);
                
                FileAppender appender = null;
                if (Validates.isEmpty(appenderClass))
                {//未配置日志增加器的，默认每天日志增加器
                    appender = new FileDailyAppender();
                }
                else
                {
                    Class<?> appenderCls = Z.cls().forName(appenderClass);
                    Object obj = Classes.newInstance(appenderCls);
                    if (obj == null || !(obj instanceof FileAppender))
                    {
                        System.out.println("日志配置["+group.getId()+"]file.appender未实现LogAppender接口");
                        return false; 
                    }
                    
                    appender = (FileAppender)obj;
                }
                
                appender.setFilePattern(config.getFilePattern());
                appender.setFileEncoding(config.getFileEncoding());
                appender.setFileMaxNum(config.getFileMaxNum());
                appender.setFileMaxSize(config.getFileMaxSize());
                appender.setFilePath(config.getFilePath());
                
                config.setFileAppender(appender);
            }
            
            if (mustClass != null)
            {//指定类配置
                LoggerWapper map = mustClassMap.get(config.getMustClass());
                if (map == null)
                {
                    map = new LoggerWapper();
                    mustClassMap.put(config.getMustClass(), map);
                }
                
                putWapper(config, map);
            }
            else if (Validates.isNotEmpty(mustName))
            {//指定名称配置
                LoggerWapper map = mustNameMap.get(mustName);
                if (map == null)
                {
                    map = new LoggerWapper();
                    mustNameMap.put(mustName, map);
                }
                
                putWapper(config, map);
            }
            else
            {//缺省配置
                putWapper(config, defaultWapper);
            }
        }
        
        //更新日志工厂为当前日志服务器提供日志服务
        LogFactory.updateLogService(this);
        return true;
    }
    
    /** 设置级别对应的日志记录器 */
    private void putWapper(LogConfig config, LoggerWapper map)
    {
        int min = config.getLevelMin();
        int max = config.getLevelMax();
        
        ZhiqimLogger logger = new ZhiqimLogger();
        logger.setLevelMin(min);
        logger.setLevelMax(max);
        
        if (config.isConsoleOut())
            logger.add(config.getConsoleAppender());
        if (config.isFileOut())
            logger.add(config.getFileAppender());
        
        for (int i=min;i<=max;i++)
        {
            map.put(i, logger);
        }
    }
    
    @Override
    public void destroy() throws Exception
    {
        mustNameMap.clear();
        mustClassMap.clear();
        defaultWapper.clear();
    }
    
    /** 判断是否有日志器 */
    @Override
    public boolean isEnabled(Log log, int level)
    {
        return getLogger(log, level) != null;
    }
    
    /** 获取对应的日志器列表 */
    @Override @AnNullable
    public ZhiqimLogger getLogger(Log log, int level)
    {
        if (Validates.isNotEmpty(log.getLogName()))
        {//根据MustName查，存在配置则取配置要求
            LoggerWapper wapper = mustNameMap.get(log.getLogName());
            if (wapper == null)
                return defaultWapper.get(level);
            else
                return wapper.get(level);
        }
        else if (log.getLogClass() != null)
        {//根据MustClass查，存在配置则取配置要求
            LoggerWapper wapper = mustClassMap.get(log.getLogClass());
            if (wapper == null)
                return defaultWapper.get(level);
            else
                return wapper.get(level);
        }
        else
        {//MustName/MustClass为null的情况
            return defaultWapper.get(level);
        }
    }
}
